package com.netty.esc.config.websocket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.netty.esc.common.enums.WSProtocolTypeEM;
import com.netty.esc.common.global.UserCache;
import com.netty.esc.dao.mapper.UserMapper;
import com.netty.esc.entity.pojo.UserInfo;
import com.netty.esc.entity.send.ProtocolACKDTO;
import com.netty.esc.service.active.UserActicveAction;
import com.netty.esc.utils.TokenUtil;
import com.netty.esc.service.action.ActionCenter;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jetty.util.StringUtil;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;

import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

/**
 * @Author: zhaoyang
 * @Data: 2022/5/1 11:29
 * @Description 处理器
 */
@Component
@Slf4j
@ChannelHandler.Sharable
public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
    private WebSocketServerHandshaker handshaker;


    public static Integer PORT;

    @Resource
    TokenUtil tokenUtil;

    @Resource
    UserMapper userMapper;

    @Resource
    WebSocketManager webSocketManager;

    @Resource
    private ActionCenter actionCenter;
    @Resource
    private UserActicveAction userActicveAction;


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        // http请求，建立握手连接
        if (msg instanceof FullHttpRequest) {
            FullHttpRequest full = (FullHttpRequest) msg;
            log.info("握手链接进入，消息体为：{}", full.headers());
//            String robotId = full.headers().get("robotId");
//            String robotType = full.headers().get("robotType");
            String token = full.headers().get("token");
//            channelStart(ctx, robotId, robotType);
            //判断请求如何符合要求，不符合的话，拒绝并结束当前请求
            if (!channelStart(ctx, token)){
                throw new Exception("");
            }
            JSONObject jsonObject = new JSONObject();
            //通知好友已上线
            jsonObject.put("userId",ChannelGroups.channelToRobot.get(ctx.channel().id()));
            userActicveAction.userOnline(ctx, jsonObject);
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            handleWebSocketFrame(ctx, (WebSocketFrame) msg);
        }
    }

    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, ClassNotFoundException {
        // 关闭链路的控制消息
        if (frame instanceof CloseWebSocketFrame) {
            System.out.println("收到关闭请求");
            webSocketManager.ctxClose(ctx);
            handshaker.close(ctx.channel(), ((CloseWebSocketFrame) frame).retain());
            return;
        }
        // 维持链路的ping消息
        if (frame instanceof PingWebSocketFrame) {
            log.info("收到WebSocket原始ping");
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        // 通信双方只使用文本

        if (!(frame instanceof TextWebSocketFrame)) {
            log.error("不支持的消息类型：" + frame.getClass());
            throw new UnsupportedOperationException(String.format(
                    "%s frame types not supported", frame.getClass().getName()));
        }

        // 客户端发过了的消息
        String request = ((TextWebSocketFrame) frame).text();
        msgHandler(ctx, request);

    }

    /**
     * 消息处理器
     */
    private void msgHandler(ChannelHandlerContext ctx, String msg) {
        try {
            JSONObject jsonObject = JSON.parseObject(msg);
            String protocolType1 = jsonObject.getString("protocolType");
            if (!protocolType1.equals("011")){
                log.info("protocolType= {}, msg = {}",protocolType1,jsonObject.getString("msg"));
            }
            if (jsonObject != null) {
                String protocolType = jsonObject.getString("protocolType");
                actionCenter.execute(protocolType, ctx, jsonObject);
            } else {
                ProtocolACKDTO protocolACKDTO = new ProtocolACKDTO();
                protocolACKDTO.setProtocolType(WSProtocolTypeEM.ERROR_UNKNOWN.code());
                protocolACKDTO.setMsg("参数异常");
                WebSocketManager.sendTextMessage(ctx, protocolACKDTO);
            }
        } catch (Exception ex) {
            log.error("ex",ex);
            ProtocolACKDTO protocolACKDTO = new ProtocolACKDTO();
            protocolACKDTO.setProtocolType(WSProtocolTypeEM.ERROR_UNKNOWN.code());
            protocolACKDTO.setMsg("参数异常\n" + ex.getMessage());
            WebSocketManager.sendTextMessage(ctx, protocolACKDTO);
        }

    }

    /**
     * 链接处理器
     */
    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
        // 根据请求头Upgrade字段判断是不是websocket
        if (!req.decoderResult().isSuccess() || !"websocket".equals(req.headers().get("Upgrade"))) {
            // 400
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
            return;
        }
        // 握手工厂
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                "ws://0.0.0.0:" + PORT + "/websocket", null, false);
        // 将websocket相关的编/解码类动态添加到ChannelPipeline
        handshaker = wsFactory.newHandshaker(req);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        } else {
            System.out.println("进行握手");
            // 握手
            handshaker.handshake(ctx.channel(), req);
        }
    }

    /**
     * 链接建立
     */
//    private void channelStart(ChannelHandlerContext ctx, String robotId, String robotType) {
    private boolean channelStart(ChannelHandlerContext ctx, String token) {
        ChannelId channelId = ctx.channel().id();

        log.info("ctx为：" + ctx);
        log.info("token：{}", token);
        if (StringUtil.isBlank(token)) {
            log.error("请携带token进入");
            ProtocolACKDTO protocolACKDTO = new ProtocolACKDTO();
            protocolACKDTO.setProtocolType(WSProtocolTypeEM.ERROR_UNKNOWN.code());
            protocolACKDTO.setCode(ProtocolACKDTO.ERROR);
            protocolACKDTO.setMsg("非法参数，断开连接。");
            WebSocketManager.sendTextMessage(ctx, protocolACKDTO);
            webSocketManager.ctxClose(ctx);
            return false;
        } else {
            UserInfo userInfo = tokenVerification(ctx, token);
            if (userInfo==null){
                return false;
            }
            ChannelGroups.channelToRobot.put(channelId, userInfo.getId());
            ChannelGroups.robotToChannel.put(userInfo.getId(), channelId);
            ChannelGroups.channelsApp.add(ctx.channel());
            UserCache.addCurrentUser(userInfo);
            return true;
        }
    }

    private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {
        if (res.status().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
            HttpUtil.setContentLength(res, res.content().readableBytes());
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("链接异常断开");
        cause.printStackTrace();
        webSocketManager.ctxClose(ctx);
    }


    private UserInfo tokenVerification(ChannelHandlerContext ctx,String token){
        if (StringUtil.isBlank(token)) {
            log.error("请携带token进入");
            linkError(ctx,"请携带token进入");
            return null;
        }
        log.info("正在对token进行登录验证...");
        JSONObject object = tokenUtil.tokenAnalysis(token);
        if (Objects.isNull(object)){
            log.error("token为{}的用户token错误",token);
            linkError(ctx,"token错误...");
            return null;
        }
        String telephone = object.getString("telephone");
        UserInfo userInfo = userMapper.getUserInfoByTelephone(telephone);

        if (Objects.isNull(userInfo)){
            log.error("token为{}的用户信息不存在",token);
            linkError(ctx,"用户信息不存在...");
            return null;
        }
        return userInfo;
    }

    private void linkError(ChannelHandlerContext ctx,String msg){
        ProtocolACKDTO protocolACKDTO = new ProtocolACKDTO();
        protocolACKDTO.setProtocolType(WSProtocolTypeEM.ERROR_UNKNOWN.code());
        protocolACKDTO.setCode(ProtocolACKDTO.ERROR);
        protocolACKDTO.setMsg(msg);
        WebSocketManager.sendTextMessage(ctx, protocolACKDTO);
        webSocketManager.ctxClose(ctx);
    }

}

